package games

import (
	"github.com/shopspring/decimal"
	"go.uber.org/zap"
	"sync"
	"sync/atomic"
	"wzgames/internal/codec/sfs"
	"wzgames/internal/constant"
	"wzgames/internal/gvalidator"
	"wzgames/internal/model/gamep"
	"wzgames/internal/response"
	"wzgames/pkg/logger"
	"wzgames/pkg/server/ws"
	"wzgames/pkg/utils/stringutil"
)

// 游戏实例基础结构

type GGame struct {
	sync.RWMutex

	GameInst *gamep.GameInst
}

func (gg *GGame) SetGameInst(inst *gamep.GameInst) {
	gg.Lock()
	gg.GameInst = inst
	gg.Unlock()
}

func (gg *GGame) GetGameId() int32 {
	return gg.GameInst.GameID
}

func (gg *GGame) GameInstId() int64 {
	return gg.GameInst.InstID
}

// CheckMessage 检查来着客户端的消息是否有效
func (gg *GGame) CheckMessage(msg *sfs.TSfsObject, agtId, instId int64, uid uint64) (int16, string) {
	data := msg.GetContent()
	if data == nil {
		return response.ErrBadRequest, constant.EmptyStr
	}

	c := data.GetUtfString(sfs.CmdKey)
	if !gg.validateCmd(c) {
		logger.Warn("[Game] illegal cmd key", zap.Int64("AgtId", agtId), zap.Uint64("Uid", uid), zap.Int64("InstID", instId), zap.String("CmdKey", c))
		return response.ErrBadRequest, constant.EmptyStr
	}
	if c == constant.KeyInit {
		return response.ErrBadRequest, constant.EmptyStr
	}

	return 0, c
}

func (gg *GGame) validateCmd(c string) bool {
	l := len(c)
	if !gvalidator.IsAlphaNumeric(c) || l < 1 || l > 64 {
		return false
	}
	return true
}

func (gg *GGame) RetErr(tb *GameTable, code int16, cmd string) {
	gg.RetErrA(tb, code, cmd, "", false)
}

func (gg *GGame) RetErrWithBala(tb *GameTable, code int16, cmd string, bala bool) {
	gg.RetErrA(tb, code, cmd, "", bala)
}

func (gg *GGame) RetErr1(tb *GameTable, code int16, cmd, betId string) {
	gg.RetErrA(tb, code, cmd, betId, false)
}

func (gg *GGame) RetErrA(tb *GameTable, code int16, cmd, betId string, bala bool) {
	if cmd == constant.ResponseBet {
		tb.Marker.SetPlay(false)
	}
	m := response.AcquireMsgO13()
	xw := sfs.XWriter{}
	defer xw.Reset()
	xw.WriteUShort(uint16(2))
	xw.WriteStringS(sfs.CmdKey, cmd)
	var params uint16 = 3
	if betId == "" || betId == "0" {
		params = 2
	}
	if bala {
		params += 1
	}
	xw.WriteSfsObjectPre(sfs.ParamId, params)
	xw.WriteShortS(sfs.KeyCode, code)
	xw.WriteStringS(sfs.KeyErrorMessage, "")
	if params == 3 {
		_betId, _ := stringutil.ToInt32(betId, 0)
		xw.WriteIntS(sfs.KeyBetId, _betId)
	}
	if bala {
		xw.WriteDoubleS(constant.KeyBalance, tb.Balance())
	}
	xw.WriteTo(m)

	tb.Send2Client(m)
}

// GetPageLimit return bet history limit and offset
func (gg *GGame) GetPageLimit(m *sfs.TSfsObject) (int64, int64) {
	data := m.GetContent()
	if data == nil {
		logger.Error("error: GetPageLimit failed to get content")
		return 0, 0
	}
	data = data.GetSFSObject(sfs.ParamId)
	if data == nil {
		logger.Error("error: GetPageLimit failed to get object")
		return 0, 0
	}
	limit, _ := stringutil.ToInt64(data.GetUtfString(constant.KeyLimit), 0)
	offset, _ := stringutil.ToInt64(data.GetUtfString(constant.KeyOffset), 0)
	if limit < 1 || limit > 100 {
		limit = 8
	}
	if offset < 0 || offset > 10000 {
		offset = 0
	}
	return limit, offset
}

// Marker //////////////////////////////////////
type Marker struct {
	balance decimal.Decimal // 余额缓存
	wv      int64
	playing bool // 是否正在某局游戏中
}

func (m *Marker) InPlay() bool {
	return m.playing
}

func (m *Marker) SetPlay(b bool) {
	m.playing = b
}

func (m *Marker) Balance() float64 {
	r, _ := m.balance.Float64()
	return r
}

func (m *Marker) BalanceStr() string {
	return m.balance.String()
}

func (m *Marker) IfBalanceEnough(betAmt string) bool {
	amt, _ := decimal.NewFromString(betAmt)
	return m.balance.Cmp(amt) != -1
}

func (m *Marker) SetBalance(balance string, wv int64) {
	if atomic.LoadInt64(&m.wv) <= wv {
		m.balance, _ = decimal.NewFromString(balance)
		atomic.StoreInt64(&m.wv, wv)
	}
}

func (m *Marker) BalanceInc(amt string) int16 {
	a, _ := decimal.NewFromString(amt)
	d := m.balance.Add(a)
	if d.IsNegative() {
		return response.ErrAmount
	}
	m.balance = d
	return 0
}

func (m *Marker) BalanceDec(amt string) int16 {
	a, _ := decimal.NewFromString(amt)
	d := m.balance.Sub(a)
	if d.IsNegative() {
		return response.ErrInsufficientBalance
	}
	m.balance = d
	return 0
}

// GameTable //////////////////////////////////////
type GameTable struct {
	Player   IPlayer             // 玩家Actor
	Sess     ws.ISession         // websocket session
	Seat     gamep.SeatInfo      // 币种等相关信息
	Inst     IGameService        // 游戏实例服务
	Marker   IMarkerService      // 游戏记录
	GameCurr *gamep.GameCurrency // 关联的币种方案
	QueIdx   int32
	login    bool
}

// Send2Client 消息发往客户端
func (gt *GameTable) Send2Client(msg interface{}) {
	gt.Sess.Send(msg)
}

func (gt *GameTable) SessID() int64 {
	return gt.Seat.SessID
}

func (gt *GameTable) InstID() int64 {
	return gt.Inst.GameInstId()
}

func (gt *GameTable) SetLogin(v bool) {
	gt.login = v
}

func (gt *GameTable) Login() bool {
	return gt.login
}

func (gt *GameTable) Clean() {
	if gt.Sess.Ok() {
		gt.Sess.Close()
	}
}
